package com.ricky.admin.util;


import cn.hutool.core.date.DateUtil;

import java.time.*;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * @Author Ricky
 * @Date 2024/6/20 14:08
 * @Description: 获取工作时间工具类
 */

public class WorkTimeUtils {
    //检查中心工作时间配置
    //上班时间
    private static final LocalTime WORK_START_TIME = LocalTime.of(9, 0, 0);
    //下班时间
    private static final LocalTime WORK_END_TIME = LocalTime.of(18, 0, 0);
    //午休时间
    private static final LocalTime NOON_BREAK_START_TIME = LocalTime.of(12, 0, 0);
    //午休结束时间
    private static final LocalTime NOON_BREAK_END_TIME = LocalTime.of(14, 0, 0);
    private static final List<String> HOLIDAYS_LIST = Collections.unmodifiableList(Arrays.asList(
            "2024-09-16", "2024-09-17", "2024-10-01", "2024-10-02", "2024-10-03", "2024-10-04", "2024-10-07"
    ));//节假日
    private static final List<String> HOLIDAYS_WORK_LIST = Collections.unmodifiableList(Arrays.asList(
            "2024-05-11","2024-06-10", "2024-09-14", "2024-09-29", "2024-10-12"
    ));//调休要补上班的周末列表

    /**
     * 获取工作时间 单位小时
     *
     * @param startTime
     * @param endTime
     * @return
     */
    public static long getWorkTime(Date startTime, Date endTime) {
        //比较结果，如果date1 < date2，返回数小于0，date1==date2返回0，date1 > date2 大于0
        if (DateUtil.compare(startTime, endTime) >= 0) {
            return 0;
        }
        LocalDateTime startDateTime = LocalDateTime.ofInstant(startTime.toInstant(), ZoneId.systemDefault());
        LocalDateTime endDateTime = LocalDateTime.ofInstant(endTime.toInstant(), ZoneId.systemDefault());
        Long diff = new Long(0);
        Long dayMillis = getDayMills();
        while (true) {
            //处理调休
            for (String holiday : HOLIDAYS_LIST) {
                if (holiday.equals(DateUtil.format(startDateTime, "yyyy-MM-dd"))) {
                    //如果是节假日，则跳过节假日
                    startDateTime = LocalDateTime.of(startDateTime.toLocalDate(), WORK_START_TIME).plusDays(1);
                    continue;
                }
            }
            if (startDateTime.getDayOfYear() > endDateTime.getDayOfYear()) {
                break;
            }
            //处理节假日补班
            for (String holiday : HOLIDAYS_WORK_LIST) {
                if (holiday.equals(DateUtil.format(startDateTime, "yyyy-MM-dd"))) {
                    //如果是节假日，则跳过节假日
                    if (startDateTime.getDayOfYear() == endDateTime.getDayOfYear()) {
                        Long diffSecond = getDiffSecond(startDateTime, endDateTime);
                        diff = diffSecond + diff;
                        return diff;
                    } else if (startDateTime.getDayOfYear() < endDateTime.getDayOfYear()) {
                        diff += dayMillis;
                        startDateTime = LocalDateTime.of(startDateTime.toLocalDate(), WORK_START_TIME).plusDays(1);
                        continue;
                    } else {
                        return diff;
                    }
                }
            }
            // 跳过周六周日
            if (startDateTime.getDayOfWeek() == DayOfWeek.SATURDAY) {
                startDateTime = LocalDateTime.of(startDateTime.toLocalDate(), WORK_START_TIME).plusDays(2);
            }
            if (startDateTime.getDayOfWeek() == DayOfWeek.SUNDAY) {
                startDateTime = LocalDateTime.of(startDateTime.toLocalDate(), WORK_START_TIME).plusDays(1);
            }
            if (endDateTime.getDayOfWeek() == DayOfWeek.SATURDAY) {
                endDateTime = LocalDateTime.of(endDateTime.toLocalDate(), WORK_START_TIME).plusDays(2);
            }
            if (endDateTime.getDayOfWeek() == DayOfWeek.SUNDAY) {
                endDateTime = LocalDateTime.of(endDateTime.toLocalDate(), WORK_START_TIME).plusDays(1);
            }
            // 跨天处理
            if (startDateTime.getDayOfYear() == endDateTime.getDayOfYear()) {
                Long diffSecond = getDiffSecond(startDateTime, endDateTime);
                diff = diffSecond + diff;
                break;
            } else if (startDateTime.getDayOfYear() < endDateTime.getDayOfYear()) {
                diff += dayMillis;
                startDateTime = LocalDateTime.of(startDateTime.toLocalDate(), WORK_START_TIME).plusDays(1);
            } else {
                break;
            }
        }
        return diff;

    }

    /**
     * 获取同一天中的两个时间中的工作时长
     *
     * @param startDateTime
     * @param endDateTime
     * @return
     */
    public static Long getDiffSecond(LocalDateTime startDateTime, LocalDateTime endDateTime) {
        LocalTime startTime = startDateTime.toLocalTime();
        LocalTime endTime = endDateTime.toLocalTime();
        // diff单位：毫秒
        Long diff = new Long(0);

        // 开始时间切移
        if (startTime.isBefore(WORK_START_TIME)) {
            startTime = WORK_START_TIME;
        } else if (startTime.isAfter(NOON_BREAK_START_TIME) && startTime.isBefore(NOON_BREAK_END_TIME)) {
            startTime = NOON_BREAK_START_TIME;
        } else if (startTime.isAfter(WORK_END_TIME)) {
            startTime = WORK_END_TIME;
        }
        // 结束时间切移
        if (endTime.isBefore(WORK_START_TIME)) {
            endTime = WORK_START_TIME;
        } else if (endTime.isAfter(NOON_BREAK_START_TIME) && endTime.isBefore(NOON_BREAK_END_TIME)) {
            endTime = NOON_BREAK_START_TIME;
        } else if (endTime.isAfter(WORK_END_TIME)) {
            endTime = WORK_END_TIME;
        }
        // 午休时间判断处理
        if (startTime.compareTo(NOON_BREAK_START_TIME) <= 0 && endTime.compareTo(NOON_BREAK_END_TIME) >= 0) {
            diff = diff + ChronoUnit.MILLIS.between(NOON_BREAK_START_TIME, NOON_BREAK_END_TIME);
        }
        diff = endTime.toSecondOfDay() * 1000 - startTime.toSecondOfDay() * 1000 - diff;

        return diff;
    }

    /**
     * 时间是否在工作时间
     *
     * @param date
     * @return
     */
    public static Boolean isWorkTime(Date date) {
        LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneOffset.of("+8"));
        //判断是否在工作日
        if (isWorkDay(date)) {
            LocalTime time = dateTime.toLocalTime();
            // 开始时间切移
            if (time.isBefore(WORK_START_TIME)) {
                return false;
            } else if (time.isAfter(NOON_BREAK_START_TIME) && time.isBefore(NOON_BREAK_END_TIME)) {
                return false;
            } else if (time.isAfter(WORK_END_TIME)) {
                return false;
            }
            return true;
        }
        return false;
    }

    /**
     * 判断是否工作日
     *
     * @param date
     * @return
     */
    public static Boolean isWorkDay(Date date) {
        //判断是否在工作日
        String formatDate = DateUtil.format(date, "yyyy-MM-dd");
        if (HOLIDAYS_WORK_LIST.contains(formatDate)) {
            return true;
        }
        if (HOLIDAYS_LIST.contains(formatDate)) {
            return false;
        }
        LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneOffset.of("+8"));
        DayOfWeek week = dateTime.getDayOfWeek();
        if (week == DayOfWeek.SATURDAY || week == DayOfWeek.SUNDAY) {
            return false;
        }
        return true;
    }

    /**
     * 获取一天工作时长
     */
    public static Long getDayMills() {
        Long morning = ChronoUnit.MILLIS.between(WORK_START_TIME, NOON_BREAK_START_TIME);
        Long afternoon = ChronoUnit.MILLIS.between(NOON_BREAK_END_TIME, WORK_END_TIME);
        return morning + afternoon;
    }

    public static void main(String[] args) {
        System.out.println(getWorkTime(DateUtil.parse("2024-05-15 09:00:00"), DateUtil.parse("2024-05-16 15:00:00")));
        // 2024-09-16 09:00:00
    }
}
